﻿using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Management;
using System.Net.NetworkInformation;
using System.Runtime.InteropServices;
using System.Text;
using System.Collections.Generic;
using System.ServiceProcess;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Storage;

namespace Citi.Helpers
{
    public class WindowsUtil
    {
        public static long GetDirectorySize(string directoryPath, bool recurse)
        {
            long size = 0;
            string[] fileEntries = Directory.GetFiles(directoryPath);

            foreach (string fileName in fileEntries)
            {
                Interlocked.Add(ref size, (new FileInfo(fileName)).Length);
            }

            if (recurse)
            {
                string[] subdirEntries = Directory.GetDirectories(directoryPath);

                Parallel.For<long>(0, subdirEntries.Length, () => 0, (i, loop, subtotal) =>
                {
                    if ((File.GetAttributes(subdirEntries[i]) & FileAttributes.ReparsePoint) != FileAttributes.ReparsePoint)
                    {
                        subtotal += GetDirectorySize(subdirEntries[i], true);
                        return subtotal;
                    }
                    return 0;
                },
                    (x) => Interlocked.Add(ref size, x)
                );
            }

            return size;
        }

        public static long GetDirectorySize(string folderPath, string userName, string password)
        {
            using (NetworkShareAccesser.Access(folderPath.Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries).First(), userName, password))
            {
                var di = new DirectoryInfo(folderPath);
                return di.EnumerateFiles("*", SearchOption.AllDirectories).Sum(fi => fi.Length);
            }
        }

        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool GetDiskFreeSpaceEx(string lpDirectoryName,
        out ulong lpFreeBytesAvailable,
        out ulong lpTotalNumberOfBytes,
        out ulong lpTotalNumberOfFreeBytes);

        public static string GetPhysicalMemory(string hostName, string userName, string password)
        {
            try
            {
                var searcher = GetManagerSearcher(hostName, userName, password, "SELECT * FROM Win32_PhysicalMemory");
                double totalMb = searcher.Get().Cast<ManagementObject>().Sum(mObj => Convert.ToDouble(mObj["Capacity"]));

                return (totalMb / 1024 / 1024) + " MB";
            }
            catch
            {
                return null;
            }
        }

        public static long GetQuotaShared(string directoryPath)
        {
            IFsrmQuotaManager quotaManager = new FsrmQuotaManager();

            try
            {
                var quota = quotaManager.CreateQuota(directoryPath);
                return quota.QuotaLimit;
            }
            catch (Exception ex)
            {
                return -1;
            } 
        }

        public static string GetSharedTotalSize(string hostName, string userName, string password, string folder)
        {
            try
            {
                var searcher = GetManagerSearcher(hostName, userName, password, string.Format("Select * from Win32_Share where Name = '{0}'", folder));
                var ret = string.Empty;

                foreach (ManagementObject mObj in searcher.Get())
                {
                    ret = mObj["Size"].ToString();
                    break;
                }

                return ret;
            }
            catch
            {
                return null;
            }
        }

        public static string GetSharedFreeSpace(string hostName, string userName, string password, string folder)
        {
            try
            {
                var searcher = GetManagerSearcher(hostName, userName, password, string.Format("Select * from Win32_Share where Name = '{0}'", folder));
                var ret = string.Empty;

                foreach (ManagementObject mObj in searcher.Get())
                {
                    ret = mObj["FreeSpace"].ToString();
                    break;
                }

                return ret;
            }
            catch
            {
                return null;
            }
        }

        public static string GetMappedTotalSize(string hostName, string userName, string password, string mappedLetter)
        {
            try
            {
                var searcher = GetManagerSearcher(hostName, userName, password, string.Format("Select * from Win32_MappedLogicalDisk where DevideId = '{0}'", mappedLetter));
                var ret = string.Empty;

                foreach (ManagementObject mObj in searcher.Get())
                {
                    ret = mObj["Size"].ToString();
                    break;
                }

                return ret;
            }
            catch
            {
                return null;
            }
        }

        public static string GetMappedFreeSpace(string hostName, string userName, string password, string mappedLetter)
        {
            try
            {
                var searcher = GetManagerSearcher(hostName, userName, password, string.Format("Select * from Win32_MappedLogicalDisk where DevideId = '{0}'", mappedLetter));
                var ret = string.Empty;

                foreach (ManagementObject mObj in searcher.Get())
                {
                    ret = mObj["FreeSpace"].ToString();
                    break;
                }

                return ret;
            }
            catch
            {
                return null;
            }
        }

        public static string GetDiskFreeSpace(string hostName, string userName, string password)
        {
            try
            {
                var searcher = GetManagerSearcher(hostName, userName, password, "SELECT * from win32_logicaldisk where deviceid = 'c:\'");
                var ret = (from ManagementObject mObj in searcher.Get() select double.Parse(mObj["FreeSpace"].ToString())).FirstOrDefault();

                return Math.Round(ret / 1024 / 1024 / 1024, 2) + " GB";
            }
            catch
            {
                return null;
            }
        }

        public static bool IsConnectedToInternet
        {
            get
            {
                var url = new Uri("www.abhisheksur.com");
                var pingurl = string.Format("{0}", url.Host);
                var host = pingurl;
                var p = new Ping();
                try
                {
                    var reply = p.Send(host, 3000);
                    if (reply.Status == IPStatus.Success)
                        return true;
                }
                catch { }

                return false;
            }
        }

        public static IPStatus GetStatus(string hostName)
        {
            //return IPStatus.Success;

            try
            {
                var pingSender = new Ping();
                var options = new PingOptions { DontFragment = true };
                const string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
                var buffer = Encoding.ASCII.GetBytes(data);
                const int timeout = 1200;
                var reply = pingSender.Send(hostName, timeout, buffer, options);

                return reply.Status;
            }
            catch
            {
                return IPStatus.BadDestination;
            }
        }

        public static string GetDiskSize(string hostName, string userName, string password)
        {
            try
            {
                var searcher = GetManagerSearcher(hostName, userName, password, "Select * from Win32_LogicalDisk");
                var ret = (from ManagementObject mObj in searcher.Get() select double.Parse(mObj["Size"].ToString())).FirstOrDefault();

                return Math.Round(ret / 1024 / 1024 / 1024, 2) + " GB";
            }
            catch
            {
                return null;
            }
        }

        public static string GetProcessorInformation(string hostName, string userName, string password)
        {
            try
            {
                var searcher = GetManagerSearcher(hostName, userName, password, "SELECT maxclockspeed, datawidth, name, manufacturer FROM Win32_Processor");
                var ret = string.Empty;

                foreach (ManagementObject mObj in searcher.Get())
                {
                    ret = (Convert.ToDecimal(mObj["maxclockspeed"]) / 1000).ToString() + "GHz ";
                    ret += mObj["datawidth"] + "bit ";
                    ret += mObj["name"].ToString();
                    break;
                }

                return ret;
            }
            catch
            {
                return null;
            }
        }

        public static string GetOperatingSystemInformation(string hostName, string userName, string password)
        {
            try
            {
                var searcher = GetManagerSearcher(hostName, userName, password, querystring: "SELECT * FROM Win32_OperatingSystem");

                var queryCollection = searcher.Get();

                var os = string.Empty;
                foreach (ManagementObject m in queryCollection)
                {

                    os = string.Format("Operating System: {0} ", m["Caption"]);
                    os += string.Format("Version: {0}", m["Version"]);
                }

                return os;
            }
            catch
            {
                return null;
            }
        }

        private static ManagementObjectSearcher GetManagerSearcher(string hostName, string userName, string password, string querystring)
        {
            try
            {
                var scope = ConnectScope(hostName, userName, password);

                var query = new ObjectQuery(querystring);
                var searcher = new ManagementObjectSearcher(scope, query);
                return searcher;
            }
            catch (Exception)
            {
                throw;
            }
        }

        private static ManagementScope ConnectScope(string hostName, string userName, string password)
        {
            //var connOptions = new ConnectionOptions
            //    {
            //        Username = userName,
            //        Password = password,
            //        Impersonation = ImpersonationLevel.Impersonate,
            //        EnablePrivileges = true
            //    };

            //var scope = new ManagementScope(string.Format("\\\\{0}\\root\\cimv2", hostName));

            try
            {
                var connOptions = new ConnectionOptions
                                {
                                    Username = userName,
                                    Password = password,
                                    Impersonation = ImpersonationLevel.Impersonate,
                                    EnablePrivileges = true,
                                    Authentication = AuthenticationLevel.Packet,
                                    Timeout = new TimeSpan(0, 0, 30)
                                };

                var managementPath = new ManagementPath { NamespacePath = @"\root\cimv2", Server = hostName };
                var scope = new ManagementScope(managementPath, connOptions);
                scope.Connect();
                return scope;
            }
            catch (Exception e)
            {
                if (e.Message != "User credentials cannot be used for local connections ")
                    throw new Exception("Access is denied");

                var scopeLocal = new ManagementScope(string.Format("\\\\{0}\\root\\cimv2", hostName));
                scopeLocal.Connect();

                return scopeLocal;
            }
        }

        public static DateTime GetLastSystemShutdown(string hostName, string userName, string password)
        {
            ManagementScope scope = null;

            try
            {
                scope = ConnectScope(hostName, userName, password);

                if (scope == null) return DateTime.MinValue;

                var objectGetOptions = new ObjectGetOptions();
                var managementPath = new ManagementPath("Win32_OperatingSystem");
                var processClass = new ManagementClass(scope, managementPath, objectGetOptions);

                string dateTime = null;

                foreach (ManagementObject item in processClass.GetInstances())
                    dateTime = item["LastBootUpTime"].ToString();

                return ManagementDateTimeConverter.ToDateTime(dateTime);
            }
            catch
            {
                return DateTime.MinValue;
            }

            //const string key = @"System\CurrentControlSet\Control\Windows";
            //Microsoft.Win32.RegistryKey registryKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(key);

            //const string valueName = "ShutdownTime";
            //var val = (byte[])registryKey.GetValue(valueName);
            //var valueAsLong = BitConverter.ToInt64(val, 0);
            //return DateTime.FromFileTime(valueAsLong);
        }

        public static List<string> GetInstalledApplications(string hostName, string userName, string password)
        {
            try
            {
                var searcher = GetManagerSearcher(hostName, userName, password, "Select * from Win32_Product");
                var applications = new List<string>();

                foreach (ManagementObject mObj in searcher.Get())
                    applications.Add(mObj["Name"].ToString());

                return applications;
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// Windows restart
        /// </summary>
        public static void Restart(string hostName = null)
        {
            StartShutDown(string.IsNullOrEmpty(hostName) ? string.Format("-f -r -t 5 /m {0}", hostName) : "-f -r -t 5");
        }

        /// <summary>
        /// Log off.
        /// </summary>
        public static void LogOff(string hostName = null)
        {
            StartShutDown(string.IsNullOrEmpty(hostName) ? string.Format("-l /m {0}", hostName) : "-l");
        }

        /// <summary>
        ///  Shutting Down Windows 
        /// </summary>
        public static void Shut(string hostName = null)
        {
            StartShutDown(string.IsNullOrEmpty(hostName) ? string.Format("-f -s -t 5 /m {0}", hostName) : "-f -s -t 5");
        }

        private static void StartShutDown(string param)
        {
            var proc = new ProcessStartInfo
                {
                    FileName = "cmd",
                    WindowStyle = ProcessWindowStyle.Hidden,
                    Arguments = "/C shutdown " + param
                };
            Process.Start(proc);
        }

        public static bool IsProcessRunning(string sProcessName)
        {
            var proc = Process.GetProcessesByName(sProcessName);
            return proc.Length > 0;
        }

        public static void RestartService(string serviceName, string machineName, int timeoutMilliseconds)
        {
            try
            {
                StopService(serviceName, machineName, timeoutMilliseconds);
            }
            catch
            {
            }
            StartService(serviceName, machineName, timeoutMilliseconds);
        }

        public static void StartService(string serviceName, string machineName, int timeoutMilliseconds)
        {
            //try
            //{
            var service = new ServiceController(serviceName, machineName);
            var timeout = TimeSpan.FromMilliseconds(timeoutMilliseconds);

            if (service.Status == ServiceControllerStatus.Running)
                return;

            service.Start();
            service.WaitForStatus(ServiceControllerStatus.Running, timeout);
            //}
            //catch { }
        }

        public static ServiceControllerStatus GetServiceStatus(string serviceName, string machineName, int timeoutMilliseconds)
        {
            try
            {
                var service = new ServiceController(serviceName, machineName);
                var timeout = TimeSpan.FromMilliseconds(timeoutMilliseconds);
                //service.WaitForStatus(ServiceControllerStatus.Stopped, timeout);

                return service.Status;
            }
            catch
            {
                return ServiceControllerStatus.Stopped;
            }
        }

        public static void StopService(string serviceName, string machineName, int timeoutMilliseconds)
        {
            //try
            //{
            var service = new ServiceController(serviceName, machineName);
            var timeout = TimeSpan.FromMilliseconds(timeoutMilliseconds);

            if (service.Status == ServiceControllerStatus.Stopped)
                return;

            service.Stop();
            service.WaitForStatus(ServiceControllerStatus.Stopped, timeout);
            //}
            //catch { }
        }

        public static string GetProcessOwner(string processName, string hostName, string userName, string password)
        {
            var searcher = GetManagerSearcher(hostName, userName, password, string.Format("Select * from Win32_Process Where Name = \"{0}\"", processName));
            var processList = searcher.Get();

            foreach (ManagementObject obj in processList)
            {
                var argList = new[] { string.Empty, string.Empty };
                int returnVal = Convert.ToInt32(obj.InvokeMethod("GetOwner", argList));

                if (returnVal != 0) continue;

                string owner = argList[1] + "\\" + argList[0];
                return owner;
            }

            return "NO OWNER";
        }

        public static string GetUserLoggedWindowsService(string processName, string hostName, string userName, string password)
        {
            string query = string.Format("select * from Win32_Service where Name = \"{0}\"", processName);
            var searcher = GetManagerSearcher(hostName, userName, password, query);
            var services = searcher.Get();

            foreach (ManagementObject service in services)
                return service.GetPropertyValue("StartName") as string;

            return null;
        }

        public static void ChangeWindowsServiceUser(string processName, string hostName, string userName, string password, string userNameChange, string passwordChange)
        {
            string query = string.Format("select * from Win32_Service where Name = \"{0}\"", processName);
            var searcher = GetManagerSearcher(hostName, userName, password, query);
            var services = searcher.Get();

            foreach (ManagementObject service in services)
            {
                var serviceName = service.GetPropertyValue("Name") as string;

                var fullServiceName = "Win32_Service.Name='";
                fullServiceName += serviceName;
                fullServiceName += "'";

                var mo = new ManagementObject(fullServiceName);
                //This will change the credentials for the service
                mo.InvokeMethod("Change", new object[] { null, null, null, null, null, null, userNameChange, passwordChange, null, null, null });

                //This will start the service
                mo.InvokeMethod("StartService", new object[] { null });
                return;
            }
        }
    }
}